package gtgold;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;

/**
 * initial shopping screen
 * 
 * @author jen, jo
 */
public class Shopping extends JDialog
{
	private static final long serialVersionUID = 1L;
	private final EnumMap<Item, JLabel> priceLabels;
	private final EnumMap<Item, JTextField> quantityFields;
	private final EnumMap<Item, JLabel> totalPriceLabels;
	private final EnumMap<Item, JLabel> totalWeightLabels;
	private final EnumMap<Item, JLabel> currentInven;
	private final JLabel totalPriceLabel, totalWeightLabel;
	private final JLabel currentCashLabel, currentWeightLabel;
	private final JButton buyButton;
	private final JButton continueButton;
	private final GameManager manager;
	private final ItemVec prices, purchases;

	/**
	 * Create a new Shopping instance
	 * 
	 * @param manager
	 *            GameManager reference to retrieve/modify GameData
	 * @param storeName
	 *            Name of this store location
	 */
	public Shopping(GameManager manager, String storeName, ItemVec prices)
	{
		super(OregonTrail.frame, storeName, true);
		this.manager = manager;
		this.prices = prices;
		this.purchases = new ItemVec();

		setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		setResizable(false);
		setPreferredSize(OregonTrail.VGA);
		setLayout(new GridLayout(1, 1));

		// Main BGJPanel
		bgImagePanel mainPanel = new bgImagePanel(
				"src/gtgold/img/shoppingBg.jpg");
		add(mainPanel, BorderLayout.CENTER);
		mainPanel.setLayout(new GridLayout(1, 1));

		// Attributes JPanel
		JPanel attrPanel = new JPanel();
		attrPanel.setLayout(new GridLayout(15, 6));

		priceLabels = new EnumMap<Item, JLabel>(Item.class);
		quantityFields = new EnumMap<Item, JTextField>(Item.class);
		totalPriceLabels = new EnumMap<Item, JLabel>(Item.class);
		totalWeightLabels = new EnumMap<Item, JLabel>(Item.class);
		currentInven = new EnumMap<Item, JLabel>(Item.class);

		// Static column headers [R1]
		attrPanel.add(new JLabel(""));
		attrPanel.add(new JLabel("<html><B>Item</B></html>"));
		attrPanel.add(new JLabel("<html><B>Price</B></html>"));
		attrPanel.add(new JLabel("<html><B>Quantity</B></html>"));
		attrPanel.add(new JLabel("<html><B>Total Price</B></html>"));
		attrPanel.add(new JLabel("<html><B>Total Weight</B></html>"));
		attrPanel.add(new JLabel("<html><B>Inventory</B></html>"));

		for (Item i : Item.values()) // [R2-R8]
		{
			if (i == Item.MONEY)
			{
				continue;
			} // Can't buy money

			priceLabels.put(i, new JLabel(formatDollars(prices.get(i))));
			JTextField field = new JTextField(25);
			field.getDocument().addDocumentListener(new myTextFieldListener(i));
			quantityFields.put(i, field);
			currentInven.put(i, new JLabel("" + gameData().inventory.get(i)));
			totalPriceLabels.put(i, new JLabel(""));
			totalWeightLabels.put(i, new JLabel(""));
			updateSingleItemFields(i);

			attrPanel.add(new JLabel(""));
			attrPanel.add(new JLabel(i.toString()));
			attrPanel.add(priceLabels.get(i));
			attrPanel.add(quantityFields.get(i));
			attrPanel.add(totalPriceLabels.get(i));
			attrPanel.add(totalWeightLabels.get(i));
			attrPanel.add(currentInven.get(i));
		}

		for (int i = 0; i < 7; i++) // [R9]
		{
			attrPanel.add(new JLabel("_______________"));
		}

		// spacing [R10]
		for (int i = 0; i < 3; i++)
		{
			attrPanel.add(new JLabel(""));
		}
		attrPanel.add(new JLabel("<html><B>TOTAL</B></html>"));
		totalPriceLabel = new JLabel("");
		totalWeightLabel = new JLabel("");
		updateTotalFields();
		attrPanel.add(totalPriceLabel);
		attrPanel.add(totalWeightLabel);

		for (int i = 0; i < 14; i++)
			attrPanel.add(new JLabel(" ")); // [R11-R13]

		// [R14]
		for (int i = 0; i < 6; i++)
			attrPanel.add(new JLabel(""));
		currentCashLabel = new JLabel("");
		attrPanel.add(currentCashLabel);
		currentWeightLabel = new JLabel("");
		attrPanel.add(currentWeightLabel);
		updateCurrentCashAndWeight(); // Initialize the text of these labels

		// [R15]
		for (int i = 0; i < 5; i++)
			attrPanel.add(new JLabel(" "));

		buyButton = new JButton("Buy");
		buyButton.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent arg0)
			{
				doPurchase();
			}
		});
		attrPanel.add(buyButton);

		continueButton = new JButton("Done");
		continueButton.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent arg0)
			{
				dispose(); // execution resumes in whatever opened the dialog
			}
		});
		// Adding JPanels together
		attrPanel.add(continueButton);
		attrPanel.setOpaque(false);
		mainPanel.add(attrPanel);
		add(mainPanel);
		// JDialog initialization
		pack();
		setLocationRelativeTo(OregonTrail.frame);
		setVisible(true);
	}

	private void doPurchase()
	{
		ItemVec inventory = manager.data.inventory;

		double totalPrice = purchases.dot(prices);

		if (totalPrice <= inventory.get(Item.MONEY))
		{
			purchases.set(Item.MONEY, -totalPrice);
			inventory.addeq(purchases);
			manager.mainScreen.updateLabels();
			// dispose(); // better user confirmation to leave window open with
			// updates

			for (JTextField e : quantityFields.values())
				e.setText("");

			updateCurrentCashAndWeight();
		} else
			manager.openInfoDialog("Not enough money!");
	}

	/***
	 * Converts a string to a positive integer.
	 * 
	 * @param s
	 *            non null string to convert.
	 * @return Resulting integer, or null on failure. Empty strings are treated
	 *         as 0.
	 */
	private static Integer stringtoPositiveInteger(String s)
	{
		if (s.length() <= 0)
		{
			return 0;
		}
		try
		{
			Integer i = Integer.parseInt(s);
			if (i >= 0)
			{
				return i;
			}
		} catch (NumberFormatException e)
		{
		}

		return null;
	}

	private static String formatDollars(double amount)
	{
		NumberFormat format = new DecimalFormat("0.00"); // Probably not proper
															// usage
		return "$" + format.format(amount);
	}

	private void updatePurchaseAmount(Item i, String newText)
	{
		Integer val = stringtoPositiveInteger(newText);

		if (val != null)
		{
			purchases.set(i, val);

			updateSingleItemFields(i);
			totalPriceLabels.get(i).setForeground(Color.BLACK);
			totalWeightLabels.get(i).setForeground(Color.BLACK);

			// Now update totals
			updateTotalFields();
		} else
		// Invalid value
		{
			totalPriceLabels.get(i).setForeground(Color.RED);
			totalWeightLabels.get(i).setForeground(Color.RED);
		}
	}

	// Functions for updating various fields
	private void updateSingleItemFields(Item i)
	{
		totalPriceLabels.get(i).setText(
				formatDollars(purchases.get(i) * prices.get(i)));
		totalWeightLabels.get(i).setText(
				"" + (int) (purchases.get(i) * i.getWeight()));
	}

	private void updateTotalFields()
	{
		totalPriceLabel.setText(formatDollars(purchases.dot(prices)));
		totalWeightLabel.setText("" + (int) purchases.weight() + " lbs");
	}

	private void updateCurrentCashAndWeight()
	{
		ItemVec inventory = manager.data.inventory;
		currentCashLabel.setText("Money: "
				+ formatDollars(inventory.get(Item.MONEY)));
		currentWeightLabel.setText("Weight: " + (int) inventory.weight()
				+ " lbs");

		for (Item i : Item.values())
		{
			if (i == Item.MONEY)
				continue;
			currentInven.get(i).setText(
					"" + (int) manager.data.inventory.get(i));
		}
	}

	private GameData gameData()
	{
		return manager.data;
	}

	// Listener class
	private class myTextFieldListener implements DocumentListener
	{
		private final Item i;

		public myTextFieldListener(Item i)
		{
			this.i = i;
		}

		private void update(DocumentEvent e)
		{
			try
			{
				javax.swing.text.Document d = e.getDocument();
				String text = d.getText(0, d.getLength());
				updatePurchaseAmount(i, text);

			} catch (BadLocationException e1)
			{
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}

		@Override
		public void changedUpdate(DocumentEvent arg0)
		{
			update(arg0);
		} // For some reason, this is never called

		@Override
		public void insertUpdate(DocumentEvent e)
		{
			update(e);
		}

		@Override
		public void removeUpdate(DocumentEvent e)
		{
			update(e);
		}
	}

}
